package com.jxpanda.commons.base.reactive;


import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.jxpanda.commons.base.Entity;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Map;

/**
 * 支持reactive 的 mybatis plus 的 mapper
 *
 * @author Panda
 */
public interface ReactiveMapper<T> extends Mapper<T> {

    /**
     * 物理上是否存在，在逻辑删除环境下，用于强校验数据是否存在
     *
     * @param tableName 表名，一般是从entity上自动解析传递过来
     * @param id        主键ID
     * @return 物理上是否存在这条数据
     */
    @Select("SELECT COUNT(*) FROM ${tableName} WHERE id = ${id}")
    Mono<Boolean> isPhysicalExisting(@Param("tableName") String tableName, @Param(Entity.ID) Serializable id);

    /**
     * 提供物理删除能力
     * 利用mybatis-plus的wrapper删除
     *
     * @param tableName 表名，一般是从entity上自动解析传递过来
     * @param wrapper   Wrapper对象
     * @return 删除是否成功
     */
    @Delete("DELETE FROM ${tableName} ${ew.customSqlSegment}")
    Mono<Boolean> destroy(@Param("tableName") String tableName, @Param(Constants.WRAPPER) Wrapper<T> wrapper);

    /**
     * 提供物理删除能力
     * 基于主键ID删除
     *
     * @param tableName 表名，一般是从entity上自动解析传递过来
     * @param id        主键ID
     * @return 删除是否成功
     */
    @Delete("DELETE FROM ${tableName} WHERE id = ${id}")
    Mono<Boolean> destroyById(@Param("tableName") String tableName, @Param(Entity.ID) Serializable id);

    /**
     * 提供物理删除能力
     * 用主键ID批量删除
     *
     * @param tableName 表名，一般是从entity上自动解析传递过来
     * @param ids       主键ID的列表
     * @return 删除是否成功
     */
    @Delete("<script>DELETE FROM ${tableName} WHERE id IN (<foreach collection=\"ids\" item=\"id\" separator=\",\">#{id}</foreach>)</script>")
    Mono<Boolean> destroyByIds(@Param("tableName") String tableName, @Param("ids") Collection<? extends Serializable> ids);

    /**
     * 提供求和能力
     *
     * @param tableName 表名，一般是从entity上自动解析传递过来
     * @param field     字段，用哪个字段求和
     * @param wrapper   Wrapper对象，筛选条件
     * @return 求和后的值
     */
    @Select("SELECT SUM(${field}) FROM ${tableName} ${ew.customSqlSegment}")
    Mono<BigDecimal> sum(@Param("tableName") String tableName, @Param("field") String field, @Param(Constants.WRAPPER) Wrapper<T> wrapper);

    /**
     * 插入一条数据
     * @param entity entity实体类
     * @return 数据库操作成功的行数（由于只操作1条数据，因此成功的话会返回1，否则返回0）
     * */
    Mono<Integer> insert(T entity);

    /**
     * 删除一条数据（如果开启了逻辑删除，则会执行逻辑删除，要强制物理删除，使用destroy接口）
     * @param id 主键ID
     * @return 数据库操作成功的行数（由于只操作1条数据，因此成功的话会返回1，否则返回0）
     * */
    Mono<Integer> deleteById(Serializable id);

    /**
     * 删除数据（如果开启了逻辑删除，则会执行逻辑删除，要强制物理删除，使用destroy接口）
     * @param columnMap 字段条件
     * @return 数据库操作成功的行数（由于只操作1条数据，因此成功的话会返回1，否则返回0）
     * */
    Mono<Integer> deleteByMap(@Param("cm") Map<String, Object> columnMap);

    /**
     * 删除数据（如果开启了逻辑删除，则会执行逻辑删除，要强制物理删除，使用destroy接口）
     * @param queryWrapper queryWrapper对象
     * @return 数据库操作成功的行数（由于只操作1条数据，因此成功的话会返回1，否则返回0）
     * */
    Mono<Integer> delete(@Param("ew") Wrapper<T> queryWrapper);

    /**
     * 根据主键ID列表，批量删除数据（如果开启了逻辑删除，则会执行逻辑删除，要强制物理删除，使用destroy接口）
     * @param idList 主键ID列表
     * @return 数据库操作成功的行数（由于只操作1条数据，因此成功的话会返回1，否则返回0）
     * */
    Mono<Integer> deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    /**
     * 根据主键ID更新一条数据
     * @param entity entity实体对象，主键ID必须有值
     * @return 数据库操作成功的行数（由于只操作1条数据，因此成功的话会返回1，否则返回0）
     * */
    Mono<Integer> updateById(@Param("et") T entity);

    /**
     * 更新数据
     * @param entity entity对象，数据模板
     * @param updateWrapper updateWrapper对象，查询条件通过这个对象传递
     * @return 数据库操作成功的行数（由于只操作1条数据，因此成功的话会返回1，否则返回0）
     * */
    Mono<Integer> update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);

    /**
     * 根据主键ID查询一条数据
     * @param id 主键ID
     * @return entity实体对象
     * */
    Mono<T> selectById(Serializable id);

    /**
     * 查询一条数据
     * @param queryWrapper queryWrapper对象
     * @return entity实体对象
     * */
    Mono<T> selectOne(@Param("ew") Wrapper<T> queryWrapper);

    /**
     * count查询
     * @param queryWrapper queryWrapper对象
     * @return entity实体对象
     * */
    Mono<Integer> selectCount(@Param("ew") Wrapper<T> queryWrapper);

    /**
     * 根据主键ID列表查询数据集合
     * @param idList 主键ID列表
     * @return 对象列表
     * */
    Flux<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    /**
     * 根据条件Map查询数据
     * @param columnMap 查询条件Map
     * @return 对象列表
     * */
    Flux<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

    /**
     * 查询数据集合
     * @param queryWrapper queryWrapper查询对象
     * @return 对象列表
     * */
    Flux<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

    /**
     * 查询数据集合（返回Map结构）
     * @param queryWrapper queryWrapper查询对象
     * @return 对象列表
     * */
    Flux<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);

    /**
     * 查询数据集合（返回Object对象）
     * @param queryWrapper queryWrapper查询对象
     * @return 对象列表
     * */
    Flux<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);

    /**
     * 分页查询
     * @param page 分页对象
     * @param queryWrapper queryWrapper查询对象
     * @return 分页对象列表
     * */
    <E extends IPage<T>> Mono<E> selectPage(E page, @Param("ew") Wrapper<T> queryWrapper);

    /**
     * 分页查询（返回Map结构）
     * @param page 分页对象
     * @param queryWrapper queryWrapper查询对象
     * @return 分页对象列表
     * */
    <E extends IPage<Map<String, Object>>> Mono<E> selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);

}
